//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#include "ctokenizer.h"

#include <buola/zz/operators.h>
#include <buola/zz/zchar.h>
#include <buola/zz/zraw.h>

namespace buola { namespace scene { namespace vrml {

CTokenizer::CTokenizer()
{
    mKeywords.Add("DEF",TOKEN_KW_DEF);
    mKeywords.Add("EXTERNPROTO",TOKEN_KW_EXTERNPROTO);
    mKeywords.Add("FALSE",TOKEN_KW_FALSE);
    mKeywords.Add("IS",TOKEN_KW_IS);
    mKeywords.Add("NULL",TOKEN_KW_NULL);
    mKeywords.Add("PROTO",TOKEN_KW_PROTO);
    mKeywords.Add("ROUTE",TOKEN_KW_ROUTE);
    mKeywords.Add("TO",TOKEN_KW_TO);
    mKeywords.Add("TRUE",TOKEN_KW_TRUE);
    mKeywords.Add("USE",TOKEN_KW_USE);
    mKeywords.Add("eventIn",TOKEN_KW_EVENTIN);
    mKeywords.Add("eventOut",TOKEN_KW_EVENTOUT);
    mKeywords.Add("exposedField",TOKEN_KW_EXPOSEDFIELD);
    mKeywords.Add("field",TOKEN_KW_FIELD);
    mKeywords.Add("Script",TOKEN_KW_SCRIPT);

    mKeywords.Add("MFColor",TOKEN_FT_MFCOLOR);
    mKeywords.Add("MFFloat",TOKEN_FT_MFFLOAT);
    mKeywords.Add("MFInt32",TOKEN_FT_MFINT32);
    mKeywords.Add("MFNode",TOKEN_FT_MFNODE);
    mKeywords.Add("MFRotation",TOKEN_FT_MFROTATION);
    mKeywords.Add("MFString",TOKEN_FT_MFSTRING);
    mKeywords.Add("MFTime",TOKEN_FT_MFTIME);
    mKeywords.Add("MFVec2f",TOKEN_FT_MFVEC2F);
    mKeywords.Add("MFVec3f",TOKEN_FT_MFVEC3F);
    mKeywords.Add("SFBool",TOKEN_FT_SFBOOL);
    mKeywords.Add("SFColor",TOKEN_FT_SFCOLOR);
    mKeywords.Add("SFFloat",TOKEN_FT_SFFLOAT);
    mKeywords.Add("SFImage",TOKEN_FT_SFIMAGE);
    mKeywords.Add("SFInt32",TOKEN_FT_SFINT32);
    mKeywords.Add("SFNode",TOKEN_FT_SFNODE);
    mKeywords.Add("SFRotation",TOKEN_FT_SFROTATION);
    mKeywords.Add("SFString",TOKEN_FT_SFSTRING);
    mKeywords.Add("SFTime",TOKEN_FT_SFTIME);
    mKeywords.Add("SFVec2f",TOKEN_FT_SFVEC2F);
    mKeywords.Add("SFVec3f",TOKEN_FT_SFVEC3F);

    mOps.Add("{",TOKEN_LEFT_BRACE);
    mOps.Add("}",TOKEN_RIGHT_BRACE);
    mOps.Add("[",TOKEN_LEFT_SQUARE);
    mOps.Add("]",TOKEN_RIGHT_SQUARE);
    mOps.Add(".",TOKEN_PERIOD);
            
    mTokenNames[TOKEN_INVALID]="INVALID";
    mTokenNames[TOKEN_SPACE]="SPACE";
    mTokenNames[TOKEN_COMMENT]="COMMENT";
    mTokenNames[TOKEN_INTEGER]="INTEGER";
    mTokenNames[TOKEN_FLOAT]="FLOAT";
    mTokenNames[TOKEN_STRING]="STRING";
    mTokenNames[TOKEN_INCOMPLETESTRING]="INCOMPLETESTRING";
    mTokenNames[TOKEN_KW_DEF]="KW_DEF";
    mTokenNames[TOKEN_KW_EXTERNPROTO]="KW_EXTERNPROTO";
    mTokenNames[TOKEN_KW_FALSE]="KW_FALSE";
    mTokenNames[TOKEN_KW_IS]="KW_IS";
    mTokenNames[TOKEN_KW_NULL]="KW_NULL";
    mTokenNames[TOKEN_KW_PROTO]="KW_PROTO";
    mTokenNames[TOKEN_KW_ROUTE]="KW_ROUTE";
    mTokenNames[TOKEN_KW_TO]="KW_TO";
    mTokenNames[TOKEN_KW_TRUE]="KW_TRUE";
    mTokenNames[TOKEN_KW_USE]="KW_USE";
    mTokenNames[TOKEN_KW_EVENTIN]="KW_EVENTIN";
    mTokenNames[TOKEN_KW_EVENTOUT]="KW_EVENTOUT";
    mTokenNames[TOKEN_KW_EXPOSEDFIELD]="KW_EXPOSEDFIELD";
    mTokenNames[TOKEN_KW_FIELD]="KW_FIELD";
    mTokenNames[TOKEN_KW_SCRIPT]="KW_SCRIPT";
    mTokenNames[TOKEN_FT_MFCOLOR]="FT_MFCOLOR";
    mTokenNames[TOKEN_FT_MFFLOAT]="FT_MFFLOAT";
    mTokenNames[TOKEN_FT_MFINT32]="FT_MFINT32";
    mTokenNames[TOKEN_FT_MFNODE]="FT_MFNODE";
    mTokenNames[TOKEN_FT_MFROTATION]="FT_MFROTATION";
    mTokenNames[TOKEN_FT_MFSTRING]="FT_MFSTRING";
    mTokenNames[TOKEN_FT_MFTIME]="FT_MFTIME";
    mTokenNames[TOKEN_FT_MFVEC2F]="FT_MFVEC2F";
    mTokenNames[TOKEN_FT_MFVEC3F]="FT_MFVEC3F";
    mTokenNames[TOKEN_FT_SFBOOL]="FT_SFBOOL";
    mTokenNames[TOKEN_FT_SFCOLOR]="FT_SFCOLOR";
    mTokenNames[TOKEN_FT_SFFLOAT]="FT_SFFLOAT";
    mTokenNames[TOKEN_FT_SFIMAGE]="FT_SFIMAGE";
    mTokenNames[TOKEN_FT_SFINT32]="FT_SFINT32";
    mTokenNames[TOKEN_FT_SFNODE]="FT_SFNODE";
    mTokenNames[TOKEN_FT_SFROTATION]="FT_SFROTATION";
    mTokenNames[TOKEN_FT_SFSTRING]="FT_SFSTRING";
    mTokenNames[TOKEN_FT_SFTIME]="FT_SFTIME";
    mTokenNames[TOKEN_FT_SFVEC2F]="FT_SFVEC2F";
    mTokenNames[TOKEN_FT_SFVEC3F]="FT_SFVEC3F";
    mTokenNames[TOKEN_LEFT_SQUARE]="LEFT_SQUARE";
    mTokenNames[TOKEN_RIGHT_SQUARE]="RIGHT_SQUARE";
    mTokenNames[TOKEN_LEFT_BRACE]="LEFT_BRACE";
    mTokenNames[TOKEN_RIGHT_BRACE]="RIGHT_BRACE";
    mTokenNames[TOKEN_PERIOD]="PERIOD";
    mTokenNames[TOKEN_IDENTIFIER]="IDENTIFIER";
    mTokenNames[TOKEN_EOI]="EOI";
}                              

void CTokenizer::AddSimpleToken(EToken pToken,TIt &pI,std::vector<CToken> &pTokens)
{
    CToken lT;
    lT.mToken=pToken;
    lT.mB=pI;
    lT.mE=++pI;
    
    pTokens.push_back(lT);
}

bool CTokenizer::Tokenize(const TIt &pB,const TIt &pE,std::vector<CToken> &pTokens)
{
    pTokens.clear();

    CCharSet<char> lIdentifierNext;
    lIdentifierNext.Add(0,0x20);
    lIdentifierNext.Add({'"','#','\'',',','.','[','\\',']','{','}',0x7F});
    lIdentifierNext.Invert();
    CCharSet<char> lIdentifierStart=lIdentifierNext;
    lIdentifierStart.Remove('0','9');
    lIdentifierStart.Remove({'+','-'});
    CCharSet<char> lWhiteSpace;
    lWhiteSpace.Add({'\r','\t','\n',' ',','});
    CCharSet<char> lStringChar;
    lStringChar.Add('\\');
    lStringChar.Add('"');
    lStringChar.Invert();
    CCharSet<char> lDigit;
    lDigit.Add('0','9');
    CCharSet<char> lHexDigit=lDigit;
    lHexDigit.Add('a','f');
    lHexDigit.Add('A','F');
    

    auto rDigit=zz::chr(lDigit);
    auto rHexDigit=zz::chr(lHexDigit);
    auto rSign=zz::chr('+')|zz::chr('-');
    auto rExp=(zz::chr('E')|zz::chr('e')) >> -rSign >> +rDigit;
    auto rHex=zz::chr('0') >> zz::chr('x') >> +rHexDigit;
    auto rFloat=((zz::chr('.') >> +rDigit)|(+rDigit >> -zz::chr('.') >> *rDigit)) >> -rExp;
    
    auto rNumber=-rSign >> (rHex|rFloat);
    auto rIdentifier=(zz::chr(lIdentifierStart) >> *zz::chr(lIdentifierNext));
    auto rString=(zz::chr('"') >> *(zz::chr(lStringChar)|(zz::chr('\\')>>zz::chr())) >> zz::chr('"'));
    auto rComment=zz::chr('#') >> *(zz::chr()-zz::chr('\n'));
    auto rSpace=zz::chr(lWhiteSpace);
    
    EToken lToken;
    auto rParser=*rSpace >> *((zz::raw(rNumber++([&lToken]{lToken=TOKEN_FLOAT;}) |
                               (mKeywords++[([&lToken](EToken pToken){lToken=pToken;})] >> !zz::chr(lIdentifierNext)) |
                               mOps++[([&lToken](EToken pToken){lToken=pToken;})] |
                               rIdentifier++([&lToken]{lToken=TOKEN_IDENTIFIER;}) |
                               rString++([&lToken]{lToken=TOKEN_STRING;}))++
          [([&pTokens,&lToken](const std::pair<TIt,TIt> &pIts){pTokens.emplace_back(lToken,pIts.first,pIts.second);})]
                        |rComment) >> *rSpace);
    
    TIt lI=pB;

    if(!zz::parse(lI,pE,rParser)) return false;
    if(lI!=pE) return false;
    
    pTokens.push_back(CToken(TOKEN_EOI,lI,lI));

    msg_info() << "new tokenizing ok\n";
    
    return true;
}

void CTokenizer::PrintTokens(TTokenIt pB, TTokenIt pE)
{
    for(auto lI=pB;lI!=pE;++lI)
    {
        msg_info() << GetTokenName(lI->mToken) << "\n";
    }
}

/*namespace vrml*/ } /*namespace scene*/ } /*namespace buola*/ }
